package hamaster.jpbc.pbc.io;

import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.PERSONAL_PREFIX;
import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.PRIVATE_PREFIX;
import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.PUBLIC_PREFIX;
import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.SIGNATURE_U;
import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.SIGNATURE_V;
import static hamaster.jpbc.pbc.io.SimpleIDFileIOImpl.SIGNATURE_W;
import hamaster.jpbc.pbc.PBC;
import hamaster.jpbc.pbc.PBCDriver;
import hamaster.jpbc.pbc.PrivateKey;
import hamaster.jpbc.pbc.PublicParameter;
import hamaster.jpbc.pbs.PersonalParameter;
import it.unisa.dia.gas.jpbc.Element;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;

public class BinaryIDIOImpl implements IDIO {

	private Properties ini;
	private byte[] idBuffer;
	private ByteArrayOutputStream out;

	private BinaryIDIOImpl() {
		this.ini = new Properties();
	}

	public BinaryIDIOImpl(ByteArrayOutputStream out) {
		this();
		this.out = out;
	}

	public BinaryIDIOImpl(byte[] idBuffer) {
		this();
		this.idBuffer = idBuffer;
	}

	@Override
	public boolean save(ID id) throws IBCIOException {
		ini.putAll(id.getPersonalParameter().toProperties(PERSONAL_PREFIX));
		ini.putAll(id.getPrivateKey().toProperties(PRIVATE_PREFIX));
		ini.putAll(id.getPublicParameter().toProperties(PUBLIC_PREFIX));
		ini.setProperty(SIGNATURE_U, PBCDriver.hex(id.getAlphaU().toBytes()));
		ini.setProperty(SIGNATURE_V, PBCDriver.hex(id.getAlphaV().toBytes()));
		ini.setProperty(SIGNATURE_W, PBCDriver.hex(id.getAlphaW().toBytes()));
		try {
			ini.store(out, null);
			return true;
		} catch (IOException e) {
			throw new IBCIOException(e);
		}
	}

	@Override
	public ID load() throws IBCIOException {
		try {
			ini.load(new ByteArrayInputStream(idBuffer));
			PersonalParameter pp = new PersonalParameter();
			PublicParameter pub = new PublicParameter();
			PrivateKey pk = new PrivateKey();
			if (!pp.setFromProperties(ini, PERSONAL_PREFIX))
				throw new IOException();
			if (!pub.setFromProperties(ini, PUBLIC_PREFIX))
				throw new IOException();
			if (!pk.setFromProperties(ini, PRIVATE_PREFIX))
				throw new IOException();
			ID id = new ID();
			id.setPersonalParameter(pp);
			id.setPrivateKey(pk);
			id.setPublicParameter(pub);
			String us = ini.getProperty(SIGNATURE_U);
			String vs = ini.getProperty(SIGNATURE_V);
			String ws = ini.getProperty(SIGNATURE_W);
			if (us == null || vs == null || ws == null)
				throw new IOException();
			PBC pbc = PBC.instance();
			Element alphaU = pbc.getPairing().getG1().newElement();
			Element alphaV = pbc.getPairing().getGT().newElement();
			Element alphaW = pbc.getPairing().getGT().newElement();
			alphaU.setFromBytes(PBCDriver.unhex(us));
			alphaV.setFromBytes(PBCDriver.unhex(vs));
			alphaW.setFromBytes(PBCDriver.unhex(ws));
			id.setAlphaU(alphaU);
			id.setAlphaV(alphaV);
			id.setAlphaW(alphaW);
			return id;
		} catch (IOException e) {
			throw new IBCIOException(e);
		}
	}
}
